/**
 *    Copyright 2009-2018 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package com.yinsin.jpabatis.executor.statement;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.Query;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import com.yinsin.jpabatis.exceptions.JpaBatisException;
import com.yinsin.jpabatis.executor.Executor;
import com.yinsin.jpabatis.mapper.BoundSql;
import com.yinsin.jpabatis.mapper.MappedStatement;
import com.yinsin.jpabatis.mapper.ParameterMapping;
import com.yinsin.jpabatis.session.DefaultSqlSession.StrictMap;
import com.yinsin.jpabatis.session.ResultHandler;
import com.yinsin.jpabatis.session.RowBounds;
import com.yinsin.jpabatis.util.CommonUtils;

/**
 * @author Clinton Begin
 */
public class SimpleStatementHandler extends BaseStatementHandler {
	private static final Logger logger = LoggerFactory.getLogger(SimpleStatementHandler.class);

	public SimpleStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter, RowBounds rowBounds, ResultHandler<?> resultHandler, BoundSql boundSql) {
		super(executor, mappedStatement, parameter, rowBounds, resultHandler, boundSql);
	}

	@Override
	public <T> T query(EntityManager transaction) throws JpaBatisException {
		String sql = getBoundSql().getSql();
		Query query = transaction.createNativeQuery(sql);
		StringBuffer paramLog = doQuery(query, transaction);
		T result = resultSetHandler.handleResultSet(query);
		if(logger.isDebugEnabled()){
			logger.debug(paramLog.toString());
		}
		return result;
	}

	@Override
	public <T> List<T> queryList(EntityManager transaction) throws JpaBatisException {
		String sql = getBoundSql().getSql();
		Query query = transaction.createNativeQuery(sql);
		StringBuffer paramLog = doQuery(query, transaction);
		List<T> result = resultSetHandler.handleResultSets(query);
		if(logger.isDebugEnabled()){
			logger.debug(paramLog.toString());
		}
		return result;
	}
	
	@Override
	public <T> Page<T> queryList(EntityManager transaction, RowBounds rowBounds) throws JpaBatisException {
		String sql = getBoundSql().getSql();
		String countSql = getBoundSql().getCountSql();
		Query query = transaction.createNativeQuery(sql);
		StringBuffer paramLog = doQuery(query, transaction);
		query.setFirstResult(rowBounds.getOffset());
		query.setMaxResults(rowBounds.getLimit());
		paramLog.append(rowBounds.getOffset()).append("(java.lang.Integer), ").append(rowBounds.getLimit()).append("(java.lang.Integer)");
		List<T> list = resultSetHandler.handleResultSets(query);
		if(logger.isDebugEnabled()){
			logger.debug(paramLog.toString());
		}
		Query countQuery = transaction.createNativeQuery(countSql);
		long total = doCountQuery(countQuery, transaction);
		int page = rowBounds.getOffset() / rowBounds.getLimit();
		Pageable pageable = new PageRequest(page, rowBounds.getLimit());
		PageImpl<T> pageImpl = new PageImpl<T>(list, pageable, total);
		return pageImpl;
	}
	
	@SuppressWarnings("unchecked")
	private StringBuffer doQuery(Query query, EntityManager transaction){
		StringBuffer paramLog = new StringBuffer("Parameter => ");
		List<ParameterMapping> paramList = boundSql.getParameterMappings();
		Object paramObj = parameterHandler.getParameterObject();
		if(paramList.size() > 0){
			Object paramValue = null;
			for(ParameterMapping parameter : paramList){
				if(paramObj instanceof StrictMap){
					paramLog.append(SetStrictParam(query, parameter, (StrictMap<Object>) paramObj));
				} else if(paramObj instanceof Map){
					paramValue = ((Map<String, Object>) paramObj).get(parameter.getProperty());
					paramLog.append(paramValue).append("(").append(parameter.getJavaType().getTypeName()).append("), ");
					query.setParameter(parameter.getProperty(), paramValue);
				} else {
					paramLog.append(paramObj).append("(").append(parameter.getJavaType().getTypeName()).append("), ");
					query.setParameter(parameter.getProperty(), paramObj);
				}
			}
		}
		return paramLog;
	}
	
	@SuppressWarnings("unchecked")
	private long doCountQuery(Query query, EntityManager transaction){
		List<ParameterMapping> paramList = boundSql.getParameterMappings();
		Object paramObj = parameterHandler.getParameterObject();
		if(paramList.size() > 0){
			Object paramValue = null;
			for(ParameterMapping parameter : paramList){
				if(paramObj instanceof StrictMap){
					SetStrictParam(query, parameter, (StrictMap<Object>) paramObj);
				} else if(paramObj instanceof Map){
					paramValue = ((Map<String, Object>) paramObj).get(parameter.getProperty());
					query.setParameter(parameter.getProperty(), paramValue);
				} else {
					query.setParameter(parameter.getProperty(), paramObj);
				}
			}
		}
		return CommonUtils.objectToLong(query.getSingleResult());
	}
	
	@SuppressWarnings("unchecked")
	private String SetStrictParam(Query query, ParameterMapping parameter, StrictMap<Object> stricpMap){
		StringBuffer paramLog = new StringBuffer();
		if(stricpMap.containsKey("list")){
			Map<String, Object> params = (Map<String, Object>) (((List<Object>)stricpMap.get("list")).get(0));
			String property = parameter.getProperty();
			String key = property;
			if(property.contains(".")){
				key = property.substring(property.lastIndexOf(".") + 1);
			}
			paramLog.append(params.get(key)).append("(").append(parameter.getJavaType().getTypeName()).append("), ");
			query.setParameter(parameter.getProperty(), params.get(key));
		} else if(stricpMap.containsKey("array")){
			
		}
		return paramLog.toString();
	}

}
